#include "mapgenerator.h"
#include <queue>
#include <chrono>

const char MapGenerator::emptychar = '_';                   // 空号符
const char MapGenerator::takePlacechar = '#';               // 占位符
const int MapGenerator::expend_dir = 8;                     // 拓展方向
const double MapGenerator::default_std_good_pro = 0.028;    // 初始密度
const double MapGenerator::spread_rate = 1.0 / 9;           // 扩展系数
const double MapGenerator::base_spread_rate = 0.07;         // 扩展基础概率
const int MapGenerator::processTime = 5;                    // 再处理次数
const double MapGenerator::origin_process_rate = 3.0 / 8;   // 再处理因子
const int MapGenerator::radius = 4;                         // 距离阈值
const double MapGenerator::decayFactor = 0.85;              // 距离递减因子
const int MapGenerator::dir[8][2] = {                       // 拓展方向
    {0,1},{0,-1},{1,0},{-1,0},
    {1,1},{1,-1},{-1,1},{-1,-1}
};

MapGenerator::MapGenerator(int width, int height, std::vector<Map_Item> &map_items,double _std_good_pro)
    : width(width),
    height(height),
    grid(width, std::vector<char>(height, emptychar)),
    gen(std::chrono::system_clock::now().time_since_epoch().count()),
    dis(0.0, 1.0),
    map_items(map_items),
    std_good_pro(_std_good_pro)
{
    if(std_good_pro < 0){std_good_pro = default_std_good_pro;}
    int len = this->map_items.size();
    double sum = 0;
    for (int i = 0; i < len; ++i) {
        sum += this->map_items[i].pro;
    }
    double alpha = std_good_pro / sum;
    for (int i = 0; i < len; ++i) {
        this->map_items[i].pro *= alpha;
    }
}

std::vector<std::vector<char>>& MapGenerator::generateMap() {
    grid = std::vector<std::vector<char>>(width, std::vector<char>(height, emptychar));
    for (int i = 0; i < width; ++i) {
        for (int j = 0; j < height; ++j) {
            if (is_valid(i, j) && grid[i][j] == emptychar) {
                // 格子为空，根据概率生成A或B
                double samble = dis(gen);
                for (auto item : map_items) {
                    double pro = item.pro;
                    samble -= pro;
                    if (samble < 0) {
                        grid[i][j] = item.item;
                        spreadItem(item.item, i, j, base_spread_rate + pow(pro, spread_rate));
                        break;
                    }
                }
                if (samble >= 0) {
                    grid[i][j] = takePlacechar;
                }
            }
        }
    }
    double rate = origin_process_rate;
    for (int t = 0; t < processTime;++t, rate *= rate) {
        processMap(rate);
    }

    for (int i = 0; i < width; ++i) {
        for (int j = 0; j < height; ++j) {
            if(grid[i][j] == takePlacechar){
                grid[i][j] = emptychar;
            }
        }
    }

    return this->grid;
}

bool MapGenerator::shouldRemove(int x, int y, double rate) {
    int cnt = 0, cnt_item = 0;
    char item = grid[x][y];
    for (int d = 0; d < expend_dir; ++d) {
        int i = x + dir[d][0], j = y + dir[d][1];
        if (is_valid(i, j)) {
            cnt += 1;
            cnt_item += (grid[i][j] == item);
        }
    }
    return cnt_item == 0 || cnt_item < cnt * rate;
}

void MapGenerator::processMap(double rate) {
    std::vector<std::vector<char>> new_grid(grid);
    for (int i = 0; i < width; ++i) {
        for (int j = 0; j < height; ++j) {
            if (is_valid(i, j)) {
                char item = grid[i][j];
                if (shouldRemove(i, j, rate)) {
                    new_grid[i][j] = emptychar;
                }
            }
        }
    }
    grid = new_grid;
}

void MapGenerator::spreadItem(char item, int x, int y, double item_pro) {
    std::queue<std::pair<int, int>> queue;
    queue.push(std::make_pair(x, y));


    while (!queue.empty()) {
        auto current = queue.front();
        queue.pop();
        int i = current.first;
        int j = current.second;
        if (grid[i][j] == item) {
            // 将当前位置的周围格子加入队列
            for (int d = 0; d < expend_dir; ++d) {
                int ni = i + dir[d][0], nj = j + dir[d][1];
                if (is_valid(ni, nj)) {
                    if (grid[ni][nj] != emptychar) { continue; }

                    double distance = std::sqrt((x - ni) * (x - ni) + (y - nj) * (y - nj));
                    if (distance >= radius) { continue; }
                    double probability = std::pow(decayFactor, distance) * item_pro;
                    double samble = dis(gen);
                    if (samble >= probability) {
                        grid[ni][nj] = takePlacechar;
                        continue;
                    }
                    grid[ni][nj] = item;
                    queue.push(std::make_pair(ni, nj));
                }
            }
        }
    }
}


